#ifndef __IMAGEN__
#define __IMAGEN__
#include <cv.h>
#include <highgui.h>

#include <cassert>
#include <iostream>
#include <cstdlib>
#include <math.h>

#include "../macros.def"

using namespace std;


class Imagen{

 public:
	Imagen();
	Imagen(const char*, nat);
	Imagen(const char*, nat, nat);
	~Imagen();
	
	bool cargarImagen();
	void guardarImagen(char*);
	unsigned char pos(nat i, nat j);
	void test();
	char * imageData() const;
//funciones de remuestreo:
	void muestreo_naif();	
	void muestreo_promedio();

	const char* name;
	
	double * transformada;
 private:
	
//	const char* name;
	IplImage* src; // imagen fuente
	IplImage* dst;	// imagen destino (aca la guardo si tengo que guardarla)
	unsigned char metodo;  //metodo de resampling
	CvSize dest;	

/* 
struct CvSize{
	int height;
	int width;
}
propia de OpenCV usada para almacenar acá el ancho/alto de la imagen
destino. Un método requiere algo de tipo CvSize así que se usa por
simplicidad.
*/
	
	//uso interno:

	char* it(nat,nat,IplImage*); // "iterador"
	unsigned long long int sumaMatriz(nat,nat,nat,nat,IplImage*);
	nat promedioMatriz(nat,nat,nat,nat,IplImage*);
	void mostrarMatriz(IplImage*);
};

Imagen::Imagen(){
	src = NULL;
	dst = NULL;
	name = NULL;
	dest.width = 0;
	dest.height = 0;
	metodo = -1;
}

Imagen::Imagen(const char* nombreImagen, nat ancho ){
	src = NULL;
	dst = NULL;
	name = nombreImagen;
	dest.width = ancho;
	dest.height = ancho;
	metodo = -1;
	this->cargarImagen();
}

Imagen::Imagen(const char* nombreImagen, nat ancho, nat coordenadas ){
	src = NULL;
	dst = NULL;
	name = nombreImagen;
	dest.width = ancho;
	dest.height = ancho;
	metodo = -1;
	this->cargarImagen();
	transformada = new double[coordenadas];
	
}

Imagen::~Imagen(){}

//bool Imagen::cargarImagen(const char* name){
bool Imagen::cargarImagen(){

	this->src = cvLoadImage(this->name, CV_LOAD_IMAGE_GRAYSCALE);
	
	if(!this->src){
		return false;
	}

	return true;
}



void Imagen::guardarImagen(char* tmp){
/*
* Guardo la imagen. Reservo la memoria para el nombre de archivo y según el método de subsampling usado 
* le agrego un sufijo loco al filename.
*/
	char* filename = new char[9+strlen(tmp)];
	strcpy(filename,"");
	strcat(filename,tmp);
	if(this->metodo == 1)
		strcat(filename, "_prom");
	else if(this->metodo == 2)
		strcat(filename, "_naif");

	strcat(filename,".jpg");
	cvSaveImage(filename,dst);
	delete filename;
}

char* Imagen::it(nat fila, nat columna, IplImage* img){
/* Bueno acá está la magia. Como openCV alinea las imagenes a un tamaño multiplo de 4pixeles ponele, entonces
* para una imagen no alineada, openCV pone basura en lo que falta apra completar ese número mágico. Entonces, 
* para las funciones que siguen, uso una especie de iterador que me deja ver a la imagen, más concretamente 
* al IplImage que es un array, como una matriz donde cada x e y es un pixel válido de la imagen: nada de basura en el
* medio. Parece una pelotudez pero me ayuda mucho en lo que sigue.
*/

	nat i = columna + fila * img->widthStep;
	return &(img->imageData[i]);
}

unsigned long long int Imagen::sumaMatriz(nat fila0, nat col0, nat filan, nat coln, IplImage* img){
/* Función auxiliar que dados los bordes superior izquierdo e inferior derecho de una matriz (en este caso
* un vector IplImage), se encarga de sumar todas los pixeles y devolverlos en un acumulador
*/

	ASSERT(fila0 <= filan && col0 <= coln, "Indices mal!");
	unsigned long long int acumulador = 0;
	
	for(unsigned int i = fila0; i < filan; i++){
		for(unsigned int j = col0; j < coln; j++){
			acumulador += (unsigned char) *(it(i,j,img)); //voy recorriendo la matriz que le pasé usando la función it(x,y,iplimage)
		}
	}


	return acumulador;
}

nat Imagen::promedioMatriz(nat fila0, nat col0, nat filan, nat coln, IplImage* img){
/* Parecida a la anterior solo que aquí toma el promedio de los valores de la submatriz en cuestión */	
	ASSERT(fila0 <= filan && col0 <= coln, "Indices mal!");

	int deltaCol = coln - col0;
	int deltaFila = filan - fila0;
	int divisor = deltaCol * deltaFila;
	
	return sumaMatriz(fila0,col0,filan,coln,img) / divisor;

}

/*
  Funciones de submuestreo de imágenes:
*/

void Imagen::muestreo_naif(){
/*Función que hace el muestreo naif, es decir, ea una imagen de tamño n y se la quiere submuestrear a tamaño k, entonces
* se calcula un "step" de forma de tomar k pixeles de esos n originales de forma equidistante (q explicación chota pero me está
* subiendo la fiebre).
*/

	//creo una imagen más chica y chequeo que la imagen destino sea de menor tamaño que la otra
	ASSERT(this->dest.width <= this->src->width, "es SUBMUESTREO, la imagen destino debe ser más chica");
	this->dst = cvCreateImage(dest, IPL_DEPTH_8U,1);

	nat step = this->src->width / this->dest.width; //el "step" o en criollo "cada cuantos pixeles de ancho o alto tengo que tomar"
	
	
	for(int i = 0; i < dest.width; i++){
		for(int j = 0; j < dest.width; j++){
			*(it(i,j,dst)) = *(it(i*step,j*step,src));
		
		}
	}
	this->metodo = 2;	
}

///*
void Imagen::muestreo_promedio(){
/* Parecida a la anterior salvo que en este caso, si la img original es de nxn y la nueva es de kxk (k < n) se divide a la imagen original en
* kxk submatrices y se toma el promedio de cada una de estas submatrices.
*/

	//creo una imagen más chica
	ASSERT(this->dest.width <= this->src->width, "es SUBMUESTREO, la imagen destino debe ser más chica");
	this->dst = cvCreateImage(dest, IPL_DEPTH_8U,1);

	nat step = this->src->width / this->dest.width; // lo mismo q antes solo que esto me determina comienzos de submatrices
	int jsrc = 0; // indices para la imagen fuente. i y j se usan para el destino. El isrc y jsrc se ven afectados por el step
	int isrc = 0;	

	for(int i = 0; i < (dest.height); i++){
		isrc = i*step;
		for(int j = 0; j < (dest.width); j++){
			jsrc = j*step;
			nat k = promedioMatriz(isrc,jsrc, isrc+step,jsrc+step,src);
			*(it(i,j,dst)) = k; 			
		}
	}

	this->metodo = 1;
}


unsigned char Imagen::pos(nat i, nat j){
	ASSERT(i<dest.width && j<dest.width, "Rangos inválidos");
	char * img = dst->imageData;
	nat v = j + i * dst->widthStep;
	return img[i];
}

char * Imagen::imageData() const {
	return dst->imageData;
}


#endif
